﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Accord.Video;
using Accord.Video.DirectShow;
using Basler.Pylon;
using Jai_FactoryDotNET;

namespace MDK.Device.Components
{
    public interface ICamera
    {
        Bitmap GetBitmap();
        event CommunicationErrorHandler CommunicationError;
    }

    public class BaslerCamera : ICamera, IDisposable
    {
        private Camera _camera = null;

        private string _id;
        private PixelDataConverter converter;
        private IntPtr _bmpBuffer = IntPtr.Zero;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="exposureTime">曝光时间,us</param>
        public BaslerCamera(string id, int exposureTime = 50000)
        {
            _id = id;
            converter = new PixelDataConverter();
            converter.OutputPixelFormat = PixelType.RGB8packed;
            ExposureTime = exposureTime;
        }

        public Bitmap GetBitmap()
        {
            var retryCount = 0;
            while (_camera == null)
            {
                var cameraInfo = CameraFinder.Enumerate().Find(t => t[CameraInfoKey.UserDefinedName] == _id);
                try
                {
                    if (cameraInfo == null)
                    {
                        if (OnCommunicationError(ErrorCodeHelper.CameraNotExist, retryCount))
                        {
                            retryCount++;
                        }
                        else
                        {
                            return null;
                        }
                    }
                    else
                    {
                        _camera = new Camera(cameraInfo);
                        _camera.Open();
                        _camera.Parameters[PLCameraInstance.MaxNumBuffer].SetValue(10);                 // 设置内存中接收图像缓冲区大小
                        _camera.Parameters[PLCamera.PixelFormat].SetValue(PLCamera.PixelFormat.BayerRG8); //设置图像格式 跟后续格式转换一致
                        _camera.Parameters[PLGigECamera.GevHeartbeatTimeout].SetValue(5000);            // 设置心跳间隔

                        _camera.Parameters[PLCamera.TriggerSelector].TrySetValue(PLCamera.TriggerSelector.AcquisitionStart);
                        _camera.Parameters[PLCamera.TriggerMode].TrySetValue(PLCamera.TriggerMode.Off);
                        _camera.Parameters[PLCamera.TriggerSelector].TrySetValue(PLCamera.TriggerSelector.FrameStart);
                        _camera.Parameters[PLCamera.TriggerMode].TrySetValue(PLCamera.TriggerMode.Off);

                        _camera.Parameters[PLCamera.AcquisitionMode].SetValue("SingleFrame");

                        ExposureTime = _exposureTime;
                        _camera.ConnectionLost += OnConnectionLost;                                     // 注册掉线回调函数
                    }
                }
                catch (Exception)
                {
                    _camera?.Close();
                    _camera = null;
                    if (OnCommunicationError(ErrorCodeHelper.CameraComError, retryCount))
                    {
                        retryCount++;
                    }
                    else
                    {
                        return null;
                    }
                }
            }

            var result = _camera.StreamGrabber.GrabOne(200, TimeoutHandling.Return);
            if (result != null)
            {
                var bufLen = result.Width * result.Height * 3;
                if (_bmpBuffer == IntPtr.Zero)
                {
                    _bmpBuffer = Marshal.AllocHGlobal(bufLen);
                }
                converter.Convert(_bmpBuffer, bufLen, result);
                var bmp = new Bitmap(new Bitmap(result.Width, result.Height, 3 * result.Width, PixelFormat.Format24bppRgb, _bmpBuffer));
                Marshal.FreeHGlobal(_bmpBuffer);
                return bmp;
            }

            return null;
        }

        private void OnConnectionLost(Object sender, EventArgs e)
        {
            while (true)
            {
                try
                {
                    _camera.Close();
                    if (OnCommunicationError(ErrorCodeHelper.CameraNotExist, 0))
                    {
                        _camera.Open();
                        break;
                    }
                    else
                    {
                        break;
                    }
                }
                catch (Exception)
                {//ignore
                }
            }
        }

        public event CommunicationErrorHandler CommunicationError;

        private long _exposureTime;
        public long ExposureTime
        {
            get { return _camera == null ? _exposureTime : _camera.Parameters[PLCamera.ExposureTimeRaw].GetValue(); }
            set
            {
                _exposureTime = value;
                if (_camera != null)
                {
                    _camera.Parameters[PLCamera.ExposureAuto].TrySetValue(PLCamera.ExposureAuto.Off);
                    _camera.Parameters[PLCamera.ExposureMode].TrySetValue(PLCamera.ExposureMode.Timed);
                    long min = _camera.Parameters[PLCamera.ExposureTimeRaw].GetMinimum();
                    long max = _camera.Parameters[PLCamera.ExposureTimeRaw].GetMaximum();
                    long incr = _camera.Parameters[PLCamera.ExposureTimeRaw].GetIncrement();
                    if (value < min)
                    {
                        value = min;
                    }
                    else if (value > max)
                    {
                        value = max;
                    }
                    else
                    {
                        value = min + (((value - min) / incr) * incr);
                    }
                    _camera.Parameters[PLCamera.ExposureTimeRaw].SetValue(value);
                }
            }
        }

        protected virtual bool OnCommunicationError(int errorcode, int retryCount)
        {
            var handler = CommunicationError;
            if (handler != null)
            {
                return handler.Invoke(this, errorcode, retryCount);
            }

            return false;
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。
                if (_bmpBuffer != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(_bmpBuffer);
                }
                _camera?.Close();

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        ~BaslerCamera()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(false);
        }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            GC.SuppressFinalize(this);
        }
        #endregion
    }

    public class JAICamera : ICamera, IDisposable
    {
        private CFactory _cFactory;
        private CCamera _camera;
        private string _id;
        private AutoResetEvent _bmpReady;
        private Bitmap _bmp;

        public JAICamera(string id)
        {
            _id = id;
            ExposureTime = 70000;
            _bmpReady = new AutoResetEvent(false);
        }

        public Bitmap GetBitmap()
        {
            try
            {
                if (_cFactory == null)
                {
                    _cFactory = new CFactory();
                    _cFactory.Open();
                    _cFactory.UpdateCameraList(CFactory.EDriverType.FilterDriver);
                    _camera = _cFactory.CameraList.Find(t => t.UserName == _id);
                    _camera.Open();
                    ExposureTime = _exposureTime;
                    _camera.NewImageDelegate += _camera_NewImageDelegate;
                }


                _camera.StartImageAcquisition(false, 10);
                _bmpReady.WaitOne();
            }
            catch (Exception)
            {
                _camera?.Close();
                _camera = null;
                _cFactory?.Close();
                _cFactory = null;
                _bmp = null;
            }

            return _bmp;
        }

        private void _camera_NewImageDelegate(ref Jai_FactoryWrapper.ImageInfo info)
        {
            var bufLen = (int) (info.SizeX * info.SizeY * 3);
            _bmp = new Bitmap((int)info.SizeX, (int)info.SizeY, bufLen, PixelFormat.Format24bppRgb, info.ImageBuffer);
            _bmp?.Save(@"c:\workspace\t.bmp");
            _bmpReady.Set();
        }

        public event CommunicationErrorHandler CommunicationError;

        private long _exposureTime;

        public long ExposureTime
        {
            get { return _camera == null ? _exposureTime : (long)_camera.GetNodeValue("ExposureTime"); }
            set
            {
                _exposureTime = value;
                if (_camera != null)
                {
                    var node = _camera.GetNode("ExposureTime");
                    var min = (long)node.Min;
                    var max = (long)node.Max;
                    var inc = (long)node.Inc;
                    if (value < min)
                    {
                        value = min;
                    }
                    else if (value > max)
                    {
                        value = max;
                    }
                    else
                    {
                        value = min + (((value - min) / inc) * inc);
                    }
                    node.Value = value;
                }
            }
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。
                _camera?.StopImageAcquisition();
                _camera?.Close();
                _camera = null;
                _cFactory?.Close();
                _cFactory = null;

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        ~JAICamera()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(false);
        }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            GC.SuppressFinalize(this);
        }
        #endregion
    }

    public class StdCamera : ICamera
    {
        private VideoCaptureDevice _device;

        private Bitmap _bmp;

        private CameraInfo _info;

        object objlock = new object();

        public StdCamera(CameraInfo info)
        {
            _info = info;
        }

        public Bitmap GetBitmap()
        {
            if (_device == null)
            {
                _device = new VideoCaptureDevice(_info.MonikerString);
                _device.VideoResolution = _device.VideoCapabilities.First();
                _device.Start();
                _device.NewFrame += _device_NewFrame;
            }

            _bmp = null;
            Bitmap bmp = null;
            while (true)
            {
                lock (objlock)
                {
                    if (_bmp != null)
                    {
                        bmp = _bmp;
                        _bmp = null;break;
                    }
                }
                
                Thread.Sleep(10);
            }
            return bmp;
        }

        private void _device_NewFrame(object sender, NewFrameEventArgs e)
        {
            lock (objlock)
            {
                _bmp?.Dispose();
                _bmp = e.Frame.Clone(new Rectangle(0,0, e.Frame.Width, e.Frame.Height), PixelFormat.Format24bppRgb);
                //GC.Collect();
            }
        }

        public event CommunicationErrorHandler CommunicationError;
        public long ExposureTime { get; set; }

        public static CameraInfo[] GetAllCamera()
        {
            return new FilterInfoCollection(FilterCategory.VideoInputDevice).Select(t => new CameraInfo()
            {
                Name = t.Name,
                MonikerString = t.MonikerString
            }).ToArray();
        }

        public class CameraInfo
        {
            public string Name { get; set; }

            public string MonikerString { get; set; }
        }
    }
}